#!/usr/bin/env python3
#
#  Copyright 2016 The TMM/AD Authors. All rights reserved.
#  Use of this source code is governed by a BSD-style license that can be
#  found in the license.txt file.
#


#  Styles.
class Style:
    """Styles."""

    NONE = 0
    BOLD = 1 << 0
    ITALIC = 1 << 1
    BLINK = 1 << 2
    UNDERLINE = 1 << 3
    INVERSE = 1 << 4
    STRIKE_THROUGH = 1 << 5


#  Colors.
class Color:
    """Colors."""

    NONE = 0
    WHITE = 1 << 0
    BLACK = 1 << 1
    RED = 1 << 2
    GREEN = 1 << 3
    BLUE = 1 << 4
    YELLOW = 1 << 5
    MAGENTA = 1 << 6
    CYAN = 1 << 7

#  Maps.
__MAP_STYLE_TO_ANSI = {
    Style.BOLD: 1,
    Style.ITALIC: 3,
    Style.BLINK: 5,
    Style.UNDERLINE: 4,
    Style.INVERSE: 7,
    Style.STRIKE_THROUGH: 9
}
__MAP_FORE_COLOR_TO_ANSI = {
    Color.WHITE: 37,
    Color.BLACK: 30,
    Color.RED: 31,
    Color.GREEN: 32,
    Color.BLUE: 34,
    Color.YELLOW: 33,
    Color.MAGENTA: 35,
    Color.CYAN: 36
}
__MAP_BACK_COLOR_TO_ANSI = {
    Color.WHITE: 47,
    Color.BLACK: 40,
    Color.RED: 41,
    Color.GREEN: 42,
    Color.BLUE: 44,
    Color.YELLOW: 43,
    Color.MAGENTA: 45,
    Color.CYAN: 46
}


def __generate_ansi_code(codes):
    """Generate ANSI code.

    :type codes: list[int]
    :rtype : str
    :param codes: The code list.
    :return: The generated code.
    """

    #  Initialize with a header.
    ret = "\033["

    for i in range(0, len(codes)):
        #  Add current code.
        ret += str(codes[i])

        if i + 1 != len(codes):
            #  Add a separator.
            ret += ";"
        else:
            #  Add the ending character.
            ret += "m"

    return ret


def format_text(text, style=Style.NONE, fore_color=Color.NONE, back_color=Color.NONE):
    """Format text with specified style and color.

    :type text: str
    :type style: int
    :type fore_color: int
    :type back_color: int
    :param text: The source text.
    :param style: The text style.
    :param fore_color: The fore color.
    :param back_color: The back color.
    :rtype : str
    :return: The formatted code.
    """

    #  Initialize codes container.
    codes = []

    #  Handle styles.
    handled = []
    for handler in [Style.BOLD, Style.ITALIC, Style.BLINK,
                    Style.UNDERLINE, Style.INVERSE, Style.STRIKE_THROUGH]:
        if (style & handler) != 0:
            handled.append(__MAP_STYLE_TO_ANSI[handler])
    codes.extend(handled)

    #  Handle fore color.
    handled = []
    for handler in [Color.WHITE, Color.BLACK, Color.RED,
                    Color.GREEN, Color.BLUE, Color.YELLOW,
                    Color.MAGENTA, Color.CYAN]:
        if (fore_color & handler) != 0:
            handled.append(__MAP_FORE_COLOR_TO_ANSI[handler])
    if len(handled) > 1:
        #  Only 1 color is allowed.
        raise ValueError("Invalid fore color.")
    codes.extend(handled)

    #  Handle back color.
    handled = []
    for handler in [Color.WHITE, Color.BLACK, Color.RED,
                    Color.GREEN, Color.BLUE, Color.YELLOW,
                    Color.MAGENTA, Color.CYAN]:
        if (back_color & handler) != 0:
            handled.append(__MAP_BACK_COLOR_TO_ANSI[handler])
    if len(handled) > 1:
        #  Only 1 color is allowed.
        raise ValueError("Invalid back color.")
    codes.extend(handled)

    #  Generate code and return.
    if len(codes) != 0:
        return __generate_ansi_code(codes) + text + __generate_ansi_code([0])
    else:
        return text
